﻿using System.Windows;
using System.Diagnostics;
using System.ComponentModel;
using System;
using LFSRecord2.Model.Undo;
using System.Collections.Generic;
using LFSRecord2.Structures;

namespace LFSRecord2.Model
{
    /// <summary>
    /// Property class for animated Double values
    /// </summary>
    public class CompLayerPropertyDouble : CompLayerPropertyBase
    {
        public double DefaultValue { get; private set; }

        private double _currentValue = 0;
        public double CurrentValue
        {
            get { return (double)_currentValue; }
            set 
            {
                if (value < MinValue)
                    value = MinValue;
                else if (value > MaxValue)
                    value = MaxValue;
                _currentValue = value;
                OnPropertyChanged("CurrentValue");
            }
        }

        public double MinValue = double.MinValue;
        public double MaxValue = double.MaxValue;

        protected override void Dispose(bool disposing)
        {
            base.Dispose(disposing);
        }

        public CompLayerPropertyDouble(CompLayerBase parentLayer, int propertyIndex, string propertyName, double defaultValue, LayerPropertyValueMeaning propertyValueMeaning)
        {
            //_keyFrames = new List<CustomKeyFrame>();
            //KeyFrames = new SortableObservableCollection<CustomKeyFrame>(_keyFrames);

            IsEditable = true;
            ParentLayer = parentLayer;
            PropertyIndex = propertyIndex;
            PropertyName = propertyName;
            PropertyValueType = LayerPropertyValueTypes.Double;
            PropertyValueMeaning = propertyValueMeaning;
            DefaultValue = defaultValue;
            _currentValue = DefaultValue;
        }

        public override void HandleNewPropertyBinding(CompLayerPropertyBase prop)
        {
            CurrentValue = ((CompLayerPropertyDouble)prop).CurrentValue;

            ParentLayer.UpdatedCurrentValues = CompLayerPropertyUpdateTypes.Position | CompLayerPropertyUpdateTypes.Rotation;
            ParentLayer.DoneUpdatingCurrentValues = !ParentLayer.DoneUpdatingCurrentValues;

            RemoveCurrentValueListener();
            prop.PropertyChanged += new PropertyChangedEventHandler(prop_PropertyChanged);
        }

        void prop_PropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            if (e.PropertyName == "CurrentValue")
            {
                //Debug.WriteLine("Bound DOUBLE property changed");
                CurrentValue = ((CompLayerPropertyDouble)sender).CurrentValue;

                ParentLayer.SetCurrentTransformMatrix();
                ParentLayer.UpdatedCurrentValues = CompLayerPropertyUpdateTypes.Rotation;
                ParentLayer.DoneUpdatingCurrentValues = !ParentLayer.DoneUpdatingCurrentValues;
            }
        }

        public override void RemoveCurrentValueListener()
        {
            if (BoundProperty != null)
                BoundProperty.PropertyChanged -= new PropertyChangedEventHandler(prop_PropertyChanged);
        }

        public override CustomKeyFrame SetKeyAtCurrentTime(int frame)
        {
            // Check if there isn't a key already
            CustomKeyFrame kf = GetKeyAtTime(frame);
            if (kf != null)
                return kf;

            //kf = SetKeyFrame(frame, (double)GetValueAtTime(frame));
            kf = SetKeyFrame(frame, _currentValue);
            if (!IsAnimated)
                IsAnimated = true;

            if ((ParentLayer.LayerType == CompLayerTypes.ReplayControl || ParentLayer.LayerType == CompLayerTypes.ReplayData) &&
                PropertyName == "Time Offset")
            {
                kf.InterpolationTypeOut = KeyFrameInterpolationTypes.AddElapsed;
            }

            return kf;
        }

        public CustomDoubleKeyFrame SetKeyFrame(int frame, double newValue)
        {
            CustomDoubleKeyFrame kf = new CustomDoubleKeyFrame(frame, newValue);
            AddKeyFrame(kf);
            return kf;
        }

        public override void AddKeyFrame(CustomKeyFrame key)
        {
            AddKeyFrame(key, true);
        }
        public override void AddKeyFrame(CustomKeyFrame key, bool autoBezier)
        {
            CustomDoubleKeyFrame newKey = (CustomDoubleKeyFrame)key;
            newKey.ParentProperty = this;
            newKey.ParentLayer = ParentLayer;

            // Insert or append the new key to the array
            int index = 0;
            bool haveInserted = false;
            foreach (CustomKeyFrame kf in KeyFrames)
            {
                if (kf.FrameNum > newKey.FrameNum)
                {
                    //Debug.WriteLine("Inserting Key at index " + index);
                    KeyFrames.Insert(index, newKey);
                    haveInserted = true;
                    break;
                }
                index++;
            }

            if (!haveInserted)
            {
                //Debug.WriteLine("Appending Key at end - " + index);
                KeyFrames.Add(newKey);
            }

            RewriteKeyFrameIndices();
            SetHasPreFrames();
            SetHasPostFrames();
            SetHasCurrentFrame();
            
            // Add event listeners
            newKey.PropertyChanged += KeyFramePropertyChangedHandler;

            if (newKey.IsSelected)
            {
                HasSelectedKeyFrames = true;
                IsSelected = true;
                ParentLayer.IsSelected = ParentLayer.HasSelectedProperties();
                if (ParentLayer.IsCollapsed)
                    ParentLayer.IsCollapsed = false;
            }
            if (!_isAnimated)
            {
                _isAnimated = true;
                OnPropertyChanged("IsAnimated");
            }

            IsEdited = true;
        }

        public override void DeleteAllKeyframes()
        {
            KeyFrames.Clear();
            IsEdited = true;
        }

        public override object GetValueAtFrame(int frame)
        {
            // If we have a binding
            if (HasBoundProperty)
            {
                return BoundProperty.GetValueAtFrame(frame);
            }

            if (!IsAnimated || KeyFrames.Count == 0)
                return CurrentValue;

            CustomDoubleKeyFrame fromKey = new CustomDoubleKeyFrame(0, ((CustomDoubleKeyFrame)KeyFrames[0]).Value);
            CustomDoubleKeyFrame toKey = null;

            foreach (CustomDoubleKeyFrame kf in KeyFrames)
            {
                if (frame < kf.FrameNum)
                {
                    toKey = kf;
                    if (fromKey == null)
                        fromKey = kf;
                    break;
                }
                fromKey = kf;
            }

            if ((fromKey.Options & CustomKeyFrameOptions.Hold) > 0)
            {
                return fromKey.Value;
            }
            else if (fromKey.InterpolationTypeOut == KeyFrameInterpolationTypes.AddElapsed)
            {
                return fromKey.Value + (frame - fromKey.FrameNum) * 1.0d / LfsRecordController.project.FPS;
            }
            else
            {
                // Hold value? (time is beyond last keyframe)
                if (toKey == null)
                    return fromKey.Value;

                // Interpolate value
                return CustomDoubleInterpolation.InterpolateValue(fromKey, toKey, frame);
            }
        }

        public override CustomKeyFrame OnCurrentValueChange()
        {
            if (!IsAnimated)
            {
                IsEdited = true;
                ParentLayer.SetCurrentTransformMatrix();
                return null;
            }

            // Insert or adjust keyframe (according to CurrentValues)
            CustomDoubleKeyFrame kf = (CustomDoubleKeyFrame)GetKeyAtTime(LfsRecordController.FrameIndex);
            if (kf == null)
            {
                kf = (CustomDoubleKeyFrame)SetKeyAtCurrentTime(LfsRecordController.FrameIndex);
                return kf;
            }
            else
            {
                kf.Value = _currentValue;
                return null;
            }
        }

        public override bool SetCurrentValue(int frame)
        {
            double newValue = (double)GetValueAtFrame(frame);
            if (CurrentValue == newValue)
                return false;

            CurrentValue = newValue;
            return true;
        }

        public void KeyFramePropertyChangedHandler(object sender, PropertyChangedEventArgs e)
        {
            CustomDoubleKeyFrame kfd, kfd2;

            if (e.PropertyName == "Value" || e.PropertyName == "FrameNum")
            {
                if (PropertyName != "FOV")
                    ParentLayer.SetCurrentTransformMatrix();
                IsEdited = true;

                // Set DistanceToNextKeyFrame (difference between this value and next)
                kfd = (CustomDoubleKeyFrame)sender;
                if (kfd.KeyFrameIndex < KeyFrames.Count - 1)
                {
                    kfd2 = (CustomDoubleKeyFrame)KeyFrames[kfd.KeyFrameIndex + 1];
                    kfd.DistanceToNextKeyFrame = kfd2.Value - kfd.Value;
                }
                else
                {
                    kfd.DistanceToNextKeyFrame = 0;
                }

                // Set DistanceToNextKeyFrame of previous keyframe
                if (kfd.KeyFrameIndex > 0)
                {
                    kfd2 = (CustomDoubleKeyFrame)KeyFrames[kfd.KeyFrameIndex - 1];
                    kfd2.DistanceToNextKeyFrame = kfd.Value - kfd2.Value;
                }
            }
            else if (e.PropertyName == "IsAnimated")
            {
                if (PropertyName != "FOV")
                    ParentLayer.SetCurrentTransformMatrix();
                IsEdited = true;
            }
            else if (e.PropertyName == "EaseInValue" ||
                     e.PropertyName == "EaseOutValue")
            {
                IsEdited = true;
            }
            else if (e.PropertyName == "IsSelected")
            {
                if (((CustomKeyFrame)sender).IsSelected)
                    HasSelectedKeyFrames = true;

                IsSelected = HasSelectedKeyFrames;
                ParentLayer.IsSelected = ParentLayer.HasSelectedProperties();
            }
        }

        public override PropertyDataBase DataStructure()
        {
            Debug.WriteLine("Creating Double Property Datastructure");

            PropertyDoubleData propertyData = new PropertyDoubleData()
            {
                ParentLayerIndex = ParentLayer.LayerIndex,
                PropertyIndex = PropertyIndex,
                IsSelected = IsSelected,
                IsAnimated = IsAnimated,
                ValueLock = ValueLock,
                BindingPropertyIndex = BindingPropertyIndex,
                BoundPropertyIgnoreParent = BoundPropertyIgnoreParent,
                NumKeyFrames = KeyFrames.Count,
                KeyFrames = new KeyFrameDataBase[KeyFrames.Count],

                CurrentValue = CurrentValue
            };

            // Populate keyframes
            for (int a = 0; a < KeyFrames.Count; a++)
            {
                propertyData.KeyFrames[a] = KeyFrames[a].DataStructure();
            }

            return propertyData;
        }
    }
}
